import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Node extends Thread {
    private static final int NO_MESSAGE = Integer.MIN_VALUE; // sentinel for "no message yet"

    final int id;
    volatile Node next;
    // in_message is protected by the lock
    int in_message;
    volatile int out_message;
    volatile boolean termination;
    volatile Integer leaderId; // null until known
    volatile int maxIdSeen; // track max id seen to learn leader at termination

    // Lock and condition for message delivery
    private final Lock lock = new ReentrantLock();
    private final Condition messageReceived = lock.newCondition();
    private boolean hasMessage = false;

    // Constructor
    public Node(int id) {
        this.id = id;
        this.next = null;
        // No message received yet; prevents premature self-election (e.g., node 0)
        this.in_message = NO_MESSAGE;
        // Per LCR, each node initially sends its own id
        this.out_message = id;
        this.termination = false;
        this.leaderId = null;
        this.maxIdSeen = id;
    }

    // Setter and Getter methods

    public void setNext(Node next) {
        this.next = next;
    }

    public void setInMessage(int message) {
        lock.lock();
        try {
            this.in_message = message;
            this.hasMessage = true;
            messageReceived.signal(); // wake up the thread waiting for a message
        } finally {
            lock.unlock();
        }
    }

    public void setOutMessage(int message) {
        this.out_message = message;
    }

    public String toString() {
        String leaderStr = leaderId == null ? "unknown" : String.valueOf(leaderId);
        return "Node " + id + " [in_message=" + in_message + ", out_message=" + out_message + ", leader=" + leaderStr
                + ", termination=" + termination + "]";
    }

    // Transmission logic handled in run() using inbox; this method is no longer
    // used.

    // Sending messages to other nodes
    public void sendMessage() {
        if (next != null) {
            next.setInMessage(this.out_message);
        }
    }

    public void run() {
        // Kick off by sending our own id exactly once
        sendMessage();
        try {
            while (!termination) {
                int msg;
                lock.lock();
                try {
                    // Wait until we receive a message
                    while (!hasMessage && !termination) {
                        messageReceived.await();
                    }
                    if (termination && !hasMessage) {
                        lock.unlock();
                        break;
                    }
                    msg = in_message;
                    hasMessage = false; // consume the message
                } finally {
                    lock.unlock();
                }

                if (msg == -1) {
                    // Learn leader and propagate termination once, then finish
                    if (leaderId == null) {
                        leaderId = maxIdSeen;
                    }
                    out_message = -1;
                    sendMessage();
                    termination = true;
                } else if (msg > id) {
                    maxIdSeen = Math.max(maxIdSeen, msg);
                    out_message = msg;
                    sendMessage();
                } else if (msg == id) {
                    leaderId = id;
                    System.out.println("Node " + id + " becomes LEADER");
                    out_message = -1;
                    sendMessage();
                    termination = true;
                } else {
                    // msg < id: discard; do not send anything
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println(this.toString());
    }
}